#!/usr/bin/python
# -*- coding: utf-8 -*-
import numpy as np
import matplotlib.pyplot as plot

# 感知机：
# 输入为实例的特征向量，输出为实例的类别，取+1和-1；
# 感知机对应于输入空间中将实例划分为正负两类的分离超平面，属于判别模型；
# 导入基于误分类的损失函数，利用梯度下降法对损失函数进行极小化；
# 感知机学习算法具有简单而易于实现的优点，分为原始形式和对偶形式。


# 原始形式
# 1. 选取初始值w0，b0;
# 2. 在训练集中随机选取(xi,yi)
# 3. 如果yi*（w * xi + b）<= 0
#    更新 w = w + n*yi*xi  其中n为学习率
#    更新 b = b + n*yi
# 4. 转2，知道所有的点被正确分类

class Perception():
    def __init__(self):
        self.learning_step = 1 # 定义学习率
        #self.max_interation = 5000 # 定义最大迭代次数

    def train(self, features, labels):
        self.w = np.zeros(len(features[0]))  #初始化全为0的向量
        self.b = 0

        interation_label = True
        interation_count = 0

        # 收敛
        while(interation_label):
            interation_label = False
            
            for index in range(len(features)):
                res = (np.dot(self.w,features[index]) + self.b) * labels[index]
                while(res <= 0):
                    interation_count = interation_count + 1
                    interation_label = True
                    self.w = self.w + self.learning_step * labels[index] * features[index]
                    self.b = self.b + self.learning_step * labels[index]
                    res = (np.dot(self.w,features[index]) + self.b)*labels[index]

        return self.w,self.b


# 对偶形式
# 1. a = 0，b = 0
# 2. 随机选取数据(xi,yi)
# 3. 如果 yi(sum(aj*yj*xj*xi + b)) <=0
#    更新 ai = ai + n
#    更新 bi = bi + n*yi
# 4. 转到2，直到所有类全部正确分类

# 对偶形式训练实例以内积形式出现，为了计算方便，可以预先将训练集中的实例的内积 \
# 先计算出来,并以矩阵的形式存储，这个矩阵叫做Gram矩阵
# G = 【xi * xj】^N*N

class DualPerception():

    def __init__(self):
        self.learning_step = 1 # 定义学习率
        #self.max_interation = 5000 # 定义最大迭代次数

    def train(self, features, labels):
        self.a = np.zeros(len(features))  #初始化全为0的向量
        self.b = 0
        self.gram = np.dot(features,features.T)


        interation_label = True
        # 收敛
        while(interation_label):
            interation_label = False

            for index in range(len(features)):
                res = 0
                for j in range(len(features)):
                    res = res + self.a[j]*labels[j]*self.gram[j][index]
                res = labels[index] * (res + self.b)
                if(res <= 0):
                    self.a[index] = self.a[index] + 1
                    self.b = self.b + labels[index]
                    interation_label = True

        # w = sum 1->N (a^i * y^i * x^i)
        w = np.dot(np.multiply(self.a,labels), features)

        return w,self.b

# 可视化
def drew(features,labels,w,b):

    fig = plot.figure()
    ax = fig.add_subplot(111)

    # 准备画边界线
    x = np.arange(0, 5, 0.5)
    plot.plot(x, (-w[0] * x - b) / w[1])

    

    possive_x = []
    possive_y = []

    negetive_x = []
    negetive_y = []

    for i in range(len(labels)):
        if(labels[i] == 1):
            possive_x.append(features[i][0])
            possive_y.append(features[i][1])
        else:
            negetive_x.append(features[i][0])
            negetive_y.append(features[i][1])
    
    plot.scatter(possive_x,possive_y,c='red')
    plot.scatter(negetive_x,negetive_y,c='green')
    #plot.legend(loc='upper right')

    plot.show()

if __name__ == '__main__':
    print("starting ....")

    features = np.array([(3,3),(4,3),(1,1)])
    labels = np.array([1,1,-1])

    
    #p = Perception()
    p = DualPerception()
    w,b = p.train(features, labels)
    
    
    print(w)
    print(b)

    drew(features, labels,w,b)


    




